/******************* ?Marvell Semiconductor, Inc., ***************************
 *
 *  Purpose:    This module has the implementation of TX functions
 *
 *  $Author: schiu $
 *
 *  $Date: 2004/11/03 $
 *
 *  $Revision: #7 $
 *
 *****************************************************************************/

#include "precomp.h"
#include "pkfuncs.h"

#include "SDCardDDK.h"
#include "txproc.h"
#include "wlan_thread.h"

//#define EQ_REPLACE_QUEUE 2
//#define EQ_PENDING_QUEUE 3 

typedef enum {
	TXEQ_SUCCESS,
	TXEQ_FAIL,
	TXEQ_REPLACE_QUEUE,
	TXEQ_EQ_PENDING_QUEUE,
	TXEQ_MAX
} TXEQ_STATUS;

static const UCHAR  wmm_tos2ac[16][8] = {
	{	// 0 0 0 0	all enabled
		AC_PRIO_BE,
		AC_PRIO_BK,
		AC_PRIO_BK,
		AC_PRIO_BE,
		AC_PRIO_VI,
		AC_PRIO_VI,
		AC_PRIO_VO,
		AC_PRIO_VO
	},
	{	// 0 0 0 1	AC_PRIO_BE disabled
		AC_PRIO_BK,
		AC_PRIO_BK,
		AC_PRIO_BK,
		AC_PRIO_BK,
		AC_PRIO_VI,
		AC_PRIO_VI,
		AC_PRIO_VO,
		AC_PRIO_VO
	},
	{	// 0 0 1 0	AC_PRIO_BK should NOT be disabled
		AC_PRIO_BE,
		AC_PRIO_BK,
		AC_PRIO_BK,
		AC_PRIO_BE,
		AC_PRIO_VI,
		AC_PRIO_VI,
		AC_PRIO_VO,
		AC_PRIO_VO
	},
	{	// 0 0 1 1	AC_PRIO_BE disabled
		AC_PRIO_BK,
		AC_PRIO_BK,
		AC_PRIO_BK,
		AC_PRIO_BK,
		AC_PRIO_VI,
		AC_PRIO_VI,
		AC_PRIO_VO,
		AC_PRIO_VO
	},
	{	// 0 1 0 0	AC_PRIO_VI disabled
		AC_PRIO_BE,
		AC_PRIO_BK,
		AC_PRIO_BK,
		AC_PRIO_BE,
		AC_PRIO_BE,
		AC_PRIO_BE,
		AC_PRIO_VO,
		AC_PRIO_VO
	},
	{	// 0 1 0 1	AC_PRIO_VI & BE	disabled
		AC_PRIO_BK,
		AC_PRIO_BK,
		AC_PRIO_BK,
		AC_PRIO_BK,
		AC_PRIO_BK,
		AC_PRIO_BK,
		AC_PRIO_VO,
		AC_PRIO_VO
	},
	{	// 0 1 1 0	AC_PRIO_VI disabled
		AC_PRIO_BE,
		AC_PRIO_BK,
		AC_PRIO_BK,
		AC_PRIO_BE,
		AC_PRIO_BE,
		AC_PRIO_BE,
		AC_PRIO_VO,
		AC_PRIO_VO
	},
	{	// 0 1 1 1	AC_PRIO_VI & BE	disabled
		AC_PRIO_BK,
		AC_PRIO_BK,
		AC_PRIO_BK,
		AC_PRIO_BK,
		AC_PRIO_BK,
		AC_PRIO_BK,
		AC_PRIO_VO,
		AC_PRIO_VO
	},
	{	// 1 0 0 0	AC_PRIO_VO disabled
		AC_PRIO_BE,
		AC_PRIO_BK,
		AC_PRIO_BK,
		AC_PRIO_BE,
		AC_PRIO_VI,
		AC_PRIO_VI,
		AC_PRIO_VI,
		AC_PRIO_VI
	},
	{	// 1 0 0 1	AC_PRIO_VO & BE disabled
		AC_PRIO_BK,
		AC_PRIO_BK,
		AC_PRIO_BK,
		AC_PRIO_BK,
		AC_PRIO_VI,
		AC_PRIO_VI,
		AC_PRIO_VI,
		AC_PRIO_VI
	},
	{	// 1 0 1 0	AC_PRIO_VO disabled
		AC_PRIO_BE,
		AC_PRIO_BK,
		AC_PRIO_BK,
		AC_PRIO_BE,
		AC_PRIO_VI,
		AC_PRIO_VI,
		AC_PRIO_VI,
		AC_PRIO_VI
	},
	{	// 1 0 1 1	AC_PRIO_VO & BE disabled
		AC_PRIO_BK,
		AC_PRIO_BK,
		AC_PRIO_BK,
		AC_PRIO_BK,
		AC_PRIO_VI,
		AC_PRIO_VI,
		AC_PRIO_VI,
		AC_PRIO_VI
	},
	{	// 1 1 0 0	AC_PRIO_VO & VI disabled
		AC_PRIO_BE,
		AC_PRIO_BK,
		AC_PRIO_BK,
		AC_PRIO_BE,
		AC_PRIO_BE,
		AC_PRIO_BE,
		AC_PRIO_BE,
		AC_PRIO_BE
	},
	{	// 1 1 0 1	AC_PRIO_VO & VI & BE disabled
		AC_PRIO_BK,
		AC_PRIO_BK,
		AC_PRIO_BK,
		AC_PRIO_BK,
		AC_PRIO_BK,
		AC_PRIO_BK,
		AC_PRIO_BK,
		AC_PRIO_BK
	},
	{	// 1 1 1 0	AC_PRIO_VO & VI disabled
		AC_PRIO_BE,
		AC_PRIO_BK,
		AC_PRIO_BK,
		AC_PRIO_BE,
		AC_PRIO_BE,
		AC_PRIO_BE,
		AC_PRIO_BE,
		AC_PRIO_BE
	},
	{	// 1 1 1 1	AC_PRIO_VO & VI & BE disabled
		AC_PRIO_BK,
		AC_PRIO_BK,
		AC_PRIO_BK,
		AC_PRIO_BK,
		AC_PRIO_BK,
		AC_PRIO_BK,
		AC_PRIO_BK,
		AC_PRIO_BK
	}
};

#define MAXMSGSIZE      90
#define MAXMSGSTRN		80

typedef struct _dbgmsg
{
	TCHAR    msgbuf[MAXMSGSIZE][MAXMSGSTRN];
	USHORT   msgid;
    USHORT   RecordSize;
} DBGMSG, *PDBGMSG;

VOID InitDbgMsg(PDBGMSG pmsg)
{
    NdisZeroMemory(pmsg, sizeof(pmsg));
    return;
}
static inline void SetMsg(DBGMSG* pmsg, const TCHAR *fmt, ...)
{
    TCHAR*  bufpt;
	USHORT  MsgHdrSize;
	va_list ap;
	
	NdisZeroMemory(pmsg->msgbuf[pmsg->msgid], sizeof(pmsg->msgbuf[pmsg->msgid]));
	swprintf(pmsg->msgbuf[pmsg->msgid], L"=->%10d: ", GetTickCount());
	MsgHdrSize = wcslen(pmsg->msgbuf[pmsg->msgid]);

    va_start(ap, fmt);
    bufpt = &pmsg->msgbuf[pmsg->msgid][MsgHdrSize];
    _vsnwprintf(bufpt, sizeof(TCHAR)*MAXMSGSTRN-MsgHdrSize, fmt, ap);		///_vsnprintf
    va_end(ap);

	pmsg->msgid = (pmsg->msgid+1)%MAXMSGSIZE;
    pmsg->RecordSize = (pmsg->RecordSize>MAXMSGSIZE)?(MAXMSGSIZE):pmsg->RecordSize++;
    
    return;
}

static void DumpMsg(DBGMSG* pmsg)
{
    USHORT i;
    for (i=0 ; i<pmsg->RecordSize ; i++) {
        RETAILMSG(1, (L"%s\r\n", pmsg->msgbuf[(pmsg->msgid+i+1)%MAXMSGSIZE]));
    }
    NdisZeroMemory(pmsg, sizeof(DBGMSG));
}

#define MAX_TX_MASK     16      ///Support up to maximum different masks types

#pragma pack(1)
//
//          Tx control node data structure 
//
//          Tx control node is used by the driver to keep ready to send Tx packet information.
//
//          Status         :
//          NPTxPacket     : NDIS_PACKET reference
//          LocalWCB       : Local WCB reference
//          BufVirtualAddr : Tx staging buffer logical address
//          BufPhyAddr     : Tx staging buffer physical address
//          NextNode       : link to next Tx control node
//
/*
typedef struct _TxCtrlNode
{
    ULONG                 Status;
    PNDIS_PACKET          NPTxPacket;
    PWCB                  LocalWCB;
    PUCHAR                BufVirtualAddr;
    NDIS_PHYSICAL_ADDRESS BufPhyAddr;
    struct _TxCtrlNode    *NextNode;
} TxCtrlNode, *PTxCtrlNode;
*/


typedef struct _TXQ_NODE
{
   PNDIS_PACKET        pPacket;
   SDIO_TX_PKT          DnldPacket;
   PUCHAR               pPktBuf;            /**< Point to the buffer to fill the payload*/
   USHORT               PktCnt;             /**< How many packets to be restored in this node*/
   struct _TXQ_NODE    *next;
} TXQ_NODE,*PTXQ_NODE, **PPTXQ_NODE; 

typedef struct _TXQ_KEEPER
{
   PTXQ_NODE head;
   PTXQ_NODE tail;
}TXQ_KEEPER,*PTXQ_KEEPER,**PPTXQ_KEEPER; 
#pragma pack()

#define InitializeTxQKeeper(QKeeper)									\
{                                                     \
   (QKeeper)->head = (QKeeper)->tail = NULL;					\
} 


#define InsertTxQNodeFromHead(QKeeper, QNode)				\
    {                                                   \
        ((PTXQ_NODE)QNode)->next = (QKeeper)->head;		\
        (QKeeper)->head = (PTXQ_NODE)(QNode);			\
        if( (QKeeper)->tail == NULL )					\
            (QKeeper)->tail = (PTXQ_NODE)(QNode);			\
    }


#define IsTxQEmpty(QKeeper) ((QKeeper)->head == NULL)


#define InsertTxQNodeAtTail(QKeeper, QNode)						\
{                                  				            \
   ((PTXQ_NODE)QNode)->next = NULL;								    \
   if( (QKeeper)->tail )                        			\
       (QKeeper)->tail->next = (PTXQ_NODE)(QNode);		\
   else                                         			\
       (QKeeper)->head = (PTXQ_NODE)(QNode);    			\
   (QKeeper)->tail = (PTXQ_NODE)(QNode);							\
}


#define PopFirstTxQNode(QKeeper)                     \
	(QKeeper)->head;                                   \
	{													 												 \
        if( (QKeeper)->head )                        \
            (QKeeper)->head = (QKeeper)->head->next; \
        if( (QKeeper)->head == NULL )                \
            (QKeeper)->tail = NULL;                  \
  }


#define PopFreeTxQNode(QNode)				\
  (PTXQ_NODE)QNode;                 \
  {                                 \
    QNode = QNode->next;            \
  }
                    
/*
#define PushFreeTxQNode(HeadNode, FreeNode)			             \
 {                                                           \
  ((PTXQ_NODE)FreeNode)->next = (PTXQ_NODE)HeadNode;         \
  ((PTXQ_NODE)FreeNode)->pPacket = NULL;                     \
  (PTXQ_NODE)HeadNode = (PTXQ_NODE)FreeNode;                 \
 }
*/
#define PushFreeTxQNode(HeadNode, FreeNode)			             \
 {                                                           \
  ((PTXQ_NODE)FreeNode)->next = (PTXQ_NODE)HeadNode;         \
  (PTXQ_NODE)HeadNode = (PTXQ_NODE)FreeNode;                 \
 }


/*
 *Tx Parameters
 */
typedef struct _TX_PARAM
{
	/* Tx Queue */
    TXQ_NODE				FreeTxQNode[MAX_TX_PACKETS];
    PTXQ_NODE				TxFreeQ;
    TXQ_KEEPER				TXPRYQ[MAX_AC_QUEUES];          /* Tx priority Queue 0,1,2,3 */
    ///TXQ_KEEPER				TXRETQ;                         /* Tx priority Queue 3 */
	//TxCtrlNode				TxNode;							/* Tx-Node */

	//121707
    TXQ_KEEPER              TxPendQKeeper[4];
    PTXQ_NODE               TxFreePendQ;
    TXQ_NODE                TxPQNode[MAX_TX_PACKETS*4];
  

	// Tx-related variables 
	WCB						Wcb;							/* WCB for Tx-packet */
	PNDIS_PACKET			SentPacket;            			/* The packet which is being sent */
    CRITICAL_SECTION		TxCriticalSection;				/* Tx Critical Section */
    ULONG					TxPacketCount;                  /* number of tx pending */
	ULONG					TxPacketSend;					/* How many packets has been sent */
	ULONG					TX_Control_Value;				/* Tx-Control Value */
    DWORD					dwTxTimeoutCount;				/* How much time since Tx has been downloaded */
	///	
	PMRVDRV_ADAPTER			pAdapter;						/* Pointer to Adapter */
	NDIS_HANDLE             MrvDrvAdapterHdl;				/* Driver Adapter handler=> to communicate with NDIS */
	///TR_TX_STATE				TxState;						/* Tx State */
	PEXECCMDNODE			pFuncBeforeTx;					/* Filter functions, called before sending packets */
    PEXECCMDNODE			pFuncAfterTx;					/* Filter functions, called after sending packets */
	int						DnLdTicket;						/* Ticket for download path */

    ULONG                   TxDnldPktCnt;                   /* How many packets has been downloaded */

    USHORT                  MaxTxAggrCnt;

    DBGMSG                  TxDbgMsg;

    HANDLE					hSendEvent;
    ULONG		            TxPktStats[TxPKTAggreCnt+1];

} TX_PARAM, *PTX_PARAM;


static TX_PARAM	TxParam;

static VOID InitializeTxNodeQ(IN PTX_PARAM pTxParam);
static inline TXEQ_STATUS TxPacketEnQueue(PTX_PARAM pTxParam,PNDIS_PACKET Packet, PSDIO_TX_PKT pDnldPacket);
static inline VOID TxPacketDeQueue(PTX_PARAM pTxParam,PPTXQ_KEEPER ppTxQKeeper,PPTXQ_NODE ppTQNode, BOOLEAN IsTest);
static inline VOID TxPacketEnQueueAtHead(PTX_PARAM pTxParam,PTXQ_KEEPER pTxQKeeper, PTXQ_NODE pTQNode);
static inline VOID ReturnTxNode(PTX_PARAM pTxParam, PTXQ_NODE pTQNode);
//static ULONG TxPacketDeQueueByLowPriority(PTX_PARAM pTxParam,PPTXQ_KEEPER ppTxQKeeper,PPTXQ_NODE ppTQNode, ULONG ac);
static ULONG TxPacketDeQueueByLowPriority(PTX_PARAM pTxParam, PPTXQ_NODE ppTQNode, ULONG ac);
///static NDIS_STATUS PrepareDownloadPacket(PTX_PARAM pTxParam,PNDIS_PACKET Packet, PSDIO_TX_PKT pDnldPacket);
static NDIS_STATUS PrepareDownloadPacket(PTX_PARAM pTxParam,PNDIS_PACKET Packet, PSDIO_TX_PKT_T pDnldPacket);
static NDIS_STATUS SendSinglePacket(IN PTX_PARAM pTxParam,	IN PNDIS_PACKET Packet,	IN PSDIO_TX_PKT pDnldPacket);
///static NDIS_STATUS InitTxNode(IN PTxCtrlNode pTxNode, PWCB pWcb); 
static inline VOID InitTxNode(PTXQ_NODE pTQNode);
static void EnQueuePending(IN PTX_PARAM pTxParam);

/**
 *  @fn: TxSend()
 *  @breif: NDIS miniport serialized send packet routine
 *      Conditions for Use: Protocol driver will call this routine to pass Tx NDIS_PACKET
 *  @param: ipTxParam   Pointer to Tx_Param
 *  @param: Packet      Packet to be sent
 *    
 *  Return Value: NDIS_STATUS_RESOURCES or NDIS_STATUS_PENDING
 * 
 *  Notes:               
 *
 *****************************************************************************/
NDIS_STATUS
TxSend(IN PVOID	ipTxParam, IN PNDIS_PACKET Packet, UINT Flag)
{
    PTX_PARAM pTxParam = (PTX_PARAM)ipTxParam;
    NDIS_STATUS Status = NDIS_STATUS_SUCCESS;

    DBGPRINT(DBG_TX, (L"+MrvDrvSend()\n"));   
    *((ULONG *)(&Packet->MacReserved[0])) = GetTickCount();


    //RETAILMSG(1, (L"TxPacketCount:%d\r\n", pTxParam->TxPacketCount));
    if(pTxParam->TxPacketCount >= (MAX_TX_PACKETS-1) )
    {   
        TXEQ_STATUS sts;    

        //DBGPRINT(DBG_ERROR,(L"Tx queue is still full (count=%d), return FAILURE for this packet\r\n",Adapter->TxPacketCount));
        sts = TxPacketEnQueue(pTxParam, Packet, NULL);

        if( sts == TXEQ_SUCCESS ) //101607 
        {
            Status = NDIS_STATUS_SUCCESS;
            goto FuncFinal;
        }	// DS_driver ++
        else if ( sts == TXEQ_EQ_PENDING_QUEUE )
        { 
            DBGPRINT(DBG_TX|DBG_HELP,(L"Pending current packet:%d\n\r",(ULONG)Packet));
			//return success otherwise the endpoint may retransmit this packet that low down the throughput.
            Status = NDIS_STATUS_PENDING;
            goto FuncFinal;
			// DS_driver --
        }  
        else
        {  
            DBGPRINT(DBG_ERROR,(L"Throw out current packet:%d\n\r",pTxParam->TxPacketCount));  
            Status = NDIS_STATUS_SUCCESS;
            goto FuncFinal;
        }
    } 


    if ( TxPacketEnQueue(pTxParam, Packet, NULL) != TXEQ_SUCCESS)
    {
        DBGPRINT(DBG_ERROR, (L"%s: Enqueue failed, %d\r\n", TEXT(__FUNCTION__), pTxParam->TxPacketCount));
        Status = NDIS_STATUS_SUCCESS;
        goto FuncFinal;
    }
    //record how many tx pkt is sent.   
    pTxParam->TxPacketSend++;
#if 0  //dralee
    DBGPRINT(DBG_TX|DBG_HELP,(L"[Marvell:MrvDrvSend] Adapter->TxPacketSend=%d\n", pTxParam->TxPacketSend));
    SetMsg(&pTxParam->TxDbgMsg, L"TxSend, %d, %xh", pTxParam->TxPacketSend, Status);
#endif 
    ///#ifndef SENDPKTS
    ///ContinueTxProcess(pTxParam);
    ///#endif ///SENDPKTS
FuncFinal:
    return Status;
} 

/** 
 *  @fn: SendNullPacket()
 *  @breif: To send a NULL packet
 *  @param ipTxParam    Pointer to Tx_Param
 *  @param pwmgr        Power Management
 */
NDIS_STATUS
SendNullPacket(IN PVOID	ipTxParam,UCHAR pwmgr )
{
    PTX_PARAM pTxParam = (PTX_PARAM)ipTxParam;
    NDIS_STATUS        sdstatus = NDIS_STATUS_SUCCESS;
    TXEQ_STATUS        IsSendPktOk;
    SDIO_TX_PKT         downloadPkt;
    PSDIO_TX_PKT_T      pPktBuf = (PSDIO_TX_PKT_T)(&downloadPkt.Buf.TxDataBuf);

    downloadPkt.Len = ADD_SDIO_PKT_HDR_LENGTH(sizeof(WCB));
    downloadPkt.Type = IF_DATA_PKT; 
    NdisZeroMemory(&pPktBuf->Wcb, sizeof(WCB));
    pPktBuf->Wcb.PktPtr = sizeof(WCB); 
    pPktBuf->Wcb.PowerMgmt = pwmgr;  
    IsSendPktOk = TxPacketEnQueue(pTxParam, NULL, &downloadPkt);
       
    if (IsSendPktOk != TXEQ_SUCCESS)
    {
        RETAILMSG(1, (L"%s: Failed to enqueue\r\n", TEXT(__FUNCTION__)));
        DBGPRINT(DBG_ERROR,(L"Send Null Pkt Fail\r\n"));  
        sdstatus = NDIS_STATUS_FAILURE; 
        goto FuncFinal;
    }
    pTxParam->TxPacketSend++;
    RETAILMSG(0, (L"%s: Calling ContinueTxProcess to send NULL pkt\r\n", TEXT(__FUNCTION__)));
    SetMsg(&pTxParam->TxDbgMsg, L"SendNullPkt");
    ///ContinueTxProcess(pTxParam);
    SetTxSoftIntr(pTxParam->pAdapter);

FuncFinal:
    return sdstatus;
}   

static inline VOID SetLastPkt(USHORT MaxTxAggrCnt, PSDIO_TX_PKT pDnldPacket)
{
    PSDIO_TX_PKT_T      pPktBufPt = (PSDIO_TX_PKT_T)&pDnldPacket->Buf.TxDataBuf;
    PUCHAR               pBufPt = (PUCHAR)&pDnldPacket->Buf.TxDataBuf;
    static USHORT       PktCnt=1;
    
    for (PktCnt=1 ; PktCnt <= MaxTxAggrCnt; PktCnt++)
    {
        if (pPktBufPt->Wcb.NextWCBPtr != 0) {
            pBufPt += pPktBufPt->Wcb.NextWCBPtr;
            pPktBufPt = (PSDIO_TX_PKT_T)pBufPt;
        } else {
            pPktBufPt->Wcb.PowerMgmt |= MRVDRV_WCB_POWER_MGMT_LAST_PACKET;
            ASSERT(((pPktBufPt->Wcb.TxControl&0x00030000)>>16) < MaxTxAggrCnt);
            break;
        }
    }
    ASSERT(PktCnt <= MaxTxAggrCnt);
}

/*
 *
 *  @fn: SendSinglePacket()
 *  @breif: TX packet handler
 *  @param:  PMRVDRV_ADAPTER Adapter
 *    
 *  Return Value:
 * 
 *  Notes:               
 *
 */
static NDIS_STATUS
SendSinglePacket(
                  IN PTX_PARAM	pTxParam,
                  IN PNDIS_PACKET Packet,
                  IN PSDIO_TX_PKT pDnldPacket
)
{
    NDIS_STATUS  Status = NDIS_STATUS_SUCCESS;
    SD_API_STATUS           sdstatus;          // intermediate status
    PEXECCMDNODE			pExecNodePt;

	pExecNodePt = pTxParam->pFuncBeforeTx;
	while (pExecNodePt != NULL) 
    {
		pExecNodePt->func.ExecFunc(pExecNodePt->func.FuncArg, pDnldPacket);
		pExecNodePt = pExecNodePt->pNext;
	}
    
    //DBGPRINT(DBG_TX|DBG_HELP, (L"%s, TxState: %d\r\n", TEXT(__FUNCTION__), pTxParam->TxState.state));
    //remember the current packet being sent
    pTxParam->SentPacket = Packet; 
    pTxParam->dwTxTimeoutCount = 0;

    if (pTxParam->TxPacketCount == 0) {
        SetLastPkt(pTxParam->MaxTxAggrCnt, pDnldPacket);
        SetMsg(&pTxParam->TxDbgMsg, L"DnPkt, With_LastPkt"); 
    } else {
        SetMsg(&pTxParam->TxDbgMsg, L"DnPkt, WO_LastPkt"); 
    }

    sdstatus = If_DownloadPkt(pTxParam->pAdapter, pDnldPacket);
    if (!SD_API_SUCCESS(sdstatus))
    {                
        DBGPRINT(DBG_TX|DBG_HELP,(L"[Marvell]TX download failed!\n"));
        pTxParam->SentPacket = NULL;    
        Status = NDIS_STATUS_FAILURE;
        goto FuncFinal;
    } 
    else 
    {
        DBGPRINT(DBG_LOAD,(L"TX download success!\n"));
    }

FuncFinal:
    pExecNodePt = pTxParam->pFuncAfterTx;
	while (pExecNodePt != NULL) 
    {
		pExecNodePt->func.ExecFunc(pExecNodePt->func.FuncArg, pDnldPacket);
		pExecNodePt = pExecNodePt->pNext;
	}

    return Status;
} 

/** 
 *  @breif: Tx_Done handler
 *  @param ipTxParam    Pointer to Tx_Param
 */
PNDIS_PACKET SetTxDone(IN PVOID	ipTxParam)
{
	PTX_PARAM pTxParam = (PTX_PARAM)ipTxParam;
	PNDIS_PACKET pPacket=NULL;

    ///Restore the Tx state back to normal
    pTxParam->dwTxTimeoutCount = 0;
    pPacket = pTxParam->SentPacket;
    pTxParam->SentPacket = NULL;  

    ///Release the download path
	ReleaseDnldTicket(pTxParam->pAdapter->pDnLdParam, pTxParam->DnLdTicket);
    SetMsg(&pTxParam->TxDbgMsg, L"Tx-Done");
    if (pTxParam->TxPacketCount > 0) {
        ///More packets are waiting
        SetTxSoftIntr(pTxParam->pAdapter);
    }
	return pPacket;
}

/**
 *  @fn: InitTxNode()
 *  @breif: Initialize the TxNode
 *  @param:  PTX_PARAM pTxParam
 *  Return Value: NDIS_STATUS_SUCCESS or NDIS_STATUS_FAILURE
 * 
 *  Notes:        
 *
 */ 
/*static NDIS_STATUS InitTxNode(IN PTxCtrlNode pTxNode, PWCB pWcb)
{
    pTxNode->BufVirtualAddr = NULL;
    pTxNode->BufPhyAddr.LowPart = 0xffffffff;
    pTxNode->BufPhyAddr.HighPart = 0xffffffff;
    pTxNode->LocalWCB = pWcb;
    pTxNode->Status = MRVDRV_TX_CTRL_NODE_STATUS_IDLE;

    return NDIS_STATUS_SUCCESS;
}*/

static inline VOID InitTxNode(PTXQ_NODE pTQNode)
{
    NdisZeroMemory(pTQNode, sizeof(TXQ_NODE));
    pTQNode->pPktBuf = (PUCHAR)(&pTQNode->DnldPacket.Buf.TxDataBuf);
    pTQNode->PktCnt = 0;
    pTQNode->DnldPacket.Type = IF_DATA_PKT;
    pTQNode->DnldPacket.Len = ADD_SDIO_PKT_HDR_LENGTH(0);
    return;
}

/** 
 *  @breif: Initialize the queue of TxNode 
 *  @param: ipTxParam    Pointer to Tx_Param
 */

void InitializeTxNodeQ(IN PTX_PARAM pTxParam)
{   
    int i; 

    pTxParam->TxFreeQ = (PTXQ_NODE)&pTxParam->FreeTxQNode[0];
    for(i=0; i<MAX_TX_PACKETS-1; i++)
    {
        pTxParam->FreeTxQNode[i].next = (PTXQ_NODE)&pTxParam->FreeTxQNode[i+1];
    }

    pTxParam->FreeTxQNode[MAX_TX_PACKETS-1].next = NULL; 
	

	// DS_driver ++ Init Tx Pending Queue keeper
	InitializeTxQKeeper(&pTxParam->TxPendQKeeper[0]);
	InitializeTxQKeeper(&pTxParam->TxPendQKeeper[1]);
	InitializeTxQKeeper(&pTxParam->TxPendQKeeper[2]);
	InitializeTxQKeeper(&pTxParam->TxPendQKeeper[3]);
	pTxParam->TxFreePendQ = (PTXQ_NODE)&pTxParam->TxPQNode[0];
	for(i=0; i<4*MAX_TX_PACKETS-1; i++) {
		pTxParam->TxPQNode[i].next = (PTXQ_NODE)&pTxParam->TxPQNode[i+1];
	}
	pTxParam->TxPQNode[4*MAX_TX_PACKETS-1].next = NULL; 
	// DS_driver -- 


    return;
}


/**
 *
 *  @fn: CleanUpSingleTxBuffer()
 *  @breif: Clean up single Tx Bbuffer
 *  @param: ipTxParam    Pointer to Tx_Param
 *    
 *  Return Value: NDIS_STATUS_SUCCESS or NDIS_STATUS_FAILURE
 * 
 *  Notes:        
 *
 */
NDIS_STATUS
CleanUpSingleTxBuffer(IN PVOID	ipTxParam)
{
    PTX_PARAM pTxParam = (PTX_PARAM)ipTxParam;
    ULONG i;
    PNDIS_PACKET pPacket;

    DBGPRINT(DBG_BUF|DBG_ERROR,(L"[Marvell]+CleanUpSingleTxBuffer()"));
    EnterCriticalSection(&pTxParam->TxCriticalSection);             
    if( pTxParam->SentPacket )
    {
        pPacket = pTxParam->SentPacket;
        pTxParam->SentPacket = NULL;
    }
    
    for(i=0; i< MAX_TX_PACKETS; i++)
    {
        PTXQ_KEEPER  pTxQKeeper;
        PTXQ_NODE    pTQNode;

        TxPacketDeQueue(pTxParam, &pTxQKeeper, &pTQNode, FALSE);
        if(pTQNode == NULL) {
            break;
        }
        ReturnTxNode(pTxParam, pTQNode);
    } 

    InitializeTxNodeQ(pTxParam); 
    //set Tx queue parameter to default 
    pTxParam->TxPacketCount=0;
    pTxParam->SentPacket = NULL;
    LeaveCriticalSection(&pTxParam->TxCriticalSection); 

    return NDIS_STATUS_SUCCESS;
}

static inline UCHAR GetPktAC(PTX_PARAM pTxParam, PNDIS_PACKET Packet)
{
    UCHAR   ac;
    UCHAR tos;
    PWMM_DESC	pWmmDesc = GetWMMDesc(pTxParam->pAdapter);

    ASSERT(pWmmDesc != NULL);
    if( pWmmDesc->enabled )
    {
        tos = wmm_get_pkt_priority( Packet );
        ac = wmm_tos2ac[pWmmDesc->acstatus][tos];

        /* Access control of the current packet not the Lowest */
        if(ac > AC_PRIO_BE)
        {
            pWmmDesc->fw_notify = 1; 
        }
    } 
  	else
  	{
		ac = 3;
  	}
    return ac;
}

static inline UCHAR GetPktPriority(PTX_PARAM pTxParam, PNDIS_PACKET Packet)
{
    UCHAR   tos = 0;
    PWMM_DESC	pWmmDesc = GetWMMDesc(pTxParam->pAdapter);

    if ( pWmmDesc->enabled )
    {
        tos = wmm_get_pkt_priority( Packet );
    }
    return tos;
}

//static inline PWCB GetLastWCBPt(USHORT MaxTxAggrCnt, PTX_MPKT_T pTxPkts)
static PWCB GetLastWCBPt(USHORT MaxTxAggrCnt, PTX_MPKT_T pTxPkts)
{
    PUCHAR  PktBufPt = pTxPkts->Pkt;
    PSDIO_TX_PKT_T  pPktPt = (PSDIO_TX_PKT_T)PktBufPt;
    USHORT          PktCnt;

    for (PktCnt = 1 ; PktCnt <= MaxTxAggrCnt ; PktCnt ++)
    {
        if (pPktPt->Wcb.NextWCBPtr == 0) {
            break;
        }
        PktBufPt += pPktPt->Wcb.NextWCBPtr;
        pPktPt = (PSDIO_TX_PKT_T)PktBufPt;
    }
    ASSERT(PktCnt <= MaxTxAggrCnt);
    return &pPktPt->Wcb;
}


static inline VOID TxGetNode(PTX_PARAM pTxParam, UCHAR ac, UINT TotalPacketLength, PTXQ_NODE* ppTQNode)
{
    PTXQ_KEEPER     pTxQKeeper;
    PTXQ_NODE       pTQNode;
    UINT            RequestLen = sizeof(WCB)+TotalPacketLength;
    PWCB            pWcb;
    
    *ppTQNode = NULL;
    ///Try to dequeue an available TxNode
    /// => If there are available TxNode, dequeue one
    /// => Otherwise, dequeue the one in the lower priority queue
    pTxQKeeper = (PTXQ_KEEPER)(&pTxParam->TXPRYQ[ac]);
    pTQNode = pTxQKeeper->tail;
    ///RETAILMSG(1, (L"%s: PendCnt: %d\r\n", TEXT(__FUNCTION__), pTxParam->TxPacketCount));

    if ((pTQNode != NULL) &&
        (pTQNode->PktCnt < pTxParam->MaxTxAggrCnt) && 
        (sizeof(SDIO_TX_PKT)-pTQNode->DnldPacket.Len)<(RequestLen+FM_TX_PKT_GAP_LEN)) {
        RETAILMSG(1, (L"==>Warning, %s: No available space, %d, %d, %d\r\n", 
            TEXT(__FUNCTION__), pTQNode->PktCnt, pTQNode->DnldPacket.Len, RequestLen));
    }
    if (pTQNode != NULL) {
        pWcb = GetLastWCBPt(pTxParam->MaxTxAggrCnt, &pTQNode->DnldPacket.Buf.TxDataBuf);
///============
/*
if ((sizeof(SDIO_TX_PKT)-pTQNode->DnldPacket.Len)<(RequestLen+FM_TX_PKT_GAP_LEN)) {
    RETAILMSG(1, (L"NewTxNode, %d, %d, %d\r\n", pTQNode->PktCnt, 
        pTQNode->DnldPacket.Len, RequestLen));
}
if ((pWcb->PowerMgmt&(MRVDRV_WCB_POWER_MGMT_LAST_PACKET|MRVDRV_WCB_POWER_MGMT_NULL_PACKET))!=0) {
    RETAILMSG(1, (L"NewTxNode, %d, %xh\r\n", pTQNode->PktCnt, pWcb->PowerMgmt));
}
*/
//if (pWcb->PktLen < TxSmallPktSize) {
//    RETAILMSG(1, (L"NewTxNode, %d, %d\r\n", pTQNode->PktCnt, pWcb->PktLen));
//}
///============
    }
    if ((pTQNode != NULL) &&
        (pTQNode->PktCnt < pTxParam->MaxTxAggrCnt) && 
        ((sizeof(SDIO_TX_PKT)-pTQNode->DnldPacket.Len)>(RequestLen+FM_TX_PKT_GAP_LEN)) &&
        ((pWcb->PowerMgmt&(MRVDRV_WCB_POWER_MGMT_LAST_PACKET|MRVDRV_WCB_POWER_MGMT_NULL_PACKET))==0) 
        //&&
        //(pWcb->PktLen > TxSmallPktSize)
        ) {
        PSDIO_TX_PKT_T      pBufPt;
        PWCB    	        pWcb;
        USHORT		        TailLen, PadLen;		/**<Used to pad the tail of the buffer, align to 4 for firmware requirement */
        ///If there are available space (length & pkt_cnt) in the last node of the queue
        /// => Reuse the node
        *ppTQNode = pTQNode;
//RETAILMSG(1, (L"%s: PktCnt: %d\r\n", TEXT(__FUNCTION__), pTQNode->PktCnt));
        /**< Update the buffer length */
        TailLen = (RequestLen + FM_TX_PKT_GAP_LEN)%4;
        PadLen = (TailLen == 0)?0:(4-TailLen);
        pTQNode->DnldPacket.Len += (RequestLen+FM_TX_PKT_GAP_LEN+PadLen);

        /**< Chain the buffer with the previous one */
        /**< Note: The pTQNode->pPktBuf has not been updated */
        pBufPt = (PSDIO_TX_PKT_T)pTQNode->pPktBuf;
        pWcb = &pBufPt->Wcb;

        /**< Update the length */
        TailLen = (sizeof(WCB) + pWcb->PktLen + FM_TX_PKT_GAP_LEN)%4;
		PadLen = (TailLen == 0)?0:(4-TailLen);
        pWcb->NextWCBPtr = sizeof(WCB) + pWcb->PktLen + FM_TX_PKT_GAP_LEN + PadLen;
        
        /**< Update the packet count & buffer pointer*/
        pTQNode->pPktBuf += pBufPt->Wcb.NextWCBPtr;
        pTQNode->PktCnt++;
        pTxParam->TxPacketCount++;
        //------
        pBufPt = (PSDIO_TX_PKT_T)pTQNode->pPktBuf;
        pWcb = &pBufPt->Wcb;
        pWcb->TxControl = (pWcb->TxControl&0xfffcffff)|(((ULONG)(pTQNode->PktCnt-1))<<16);
        //------
        
        goto FuncFinal;
    }
    if(!pTxParam->TxFreeQ)
    {
		///If there is no available TxNode
		/// & if it's Deserial driver => save the packet to a pending queue & return NDIS_STATUS_PENDING
		// DS_driver ++
		goto FuncFinal;
		// DS_driver --

		/*
        TxPacketDeQueueByLowPriority(pTxParam, &pTQNode,(ULONG)ac);
        if( pTQNode = NULL )
        {
            DBGPRINT(DBG_ERROR, (L"DequeueLowPriorityNode fail (%d)\r\n", ac));
            RETAILMSG(1, (L"Dequeue Low Priority Node Failed (%d, %d)\r\n", ac, pTxParam->TxPacketCount));
            goto FuncFinal; 
        }
		
        InitTxNode(pTQNode);
		*/
    }
    else
    {
        pTQNode = PopFreeTxQNode(pTxParam->TxFreeQ);
        InitTxNode(pTQNode);
	}
    {
    USHORT		        TailLen, PadLen;
    TailLen = (RequestLen + FM_TX_PKT_GAP_LEN)%4;
    PadLen = (TailLen == 0)?0:(4-TailLen);
    pTQNode->DnldPacket.Len += (RequestLen+FM_TX_PKT_GAP_LEN+PadLen);
    }   
    InsertTxQNodeAtTail(pTxQKeeper, pTQNode); 
    pTQNode->PktCnt++;
    pTxParam->TxPacketCount++;
    *ppTQNode = pTQNode;
FuncFinal:
    return;

}

/**
 *
 *  @fn: TxPacketEnQueue()
 *  @breif: Enqueue the packet to TxQueue
 *  @param: ipTxParam   Pointer to Tx_Param
 *  @param: Packet      Packet to be enqueued
 *  @param: pDnldPacket Packet which is generate by driver itself
 *    
 *  Return Value: TXEQ_STATUS
 * 
 *  Notes:        
 *
 */
static inline TXEQ_STATUS TxPacketEnQueue(PTX_PARAM pTxParam,PNDIS_PACKET Packet, PSDIO_TX_PKT pDnldPacket)
{
    UCHAR       ac;
    NDIS_STATUS Status;
    TXEQ_STATUS ret=TXEQ_SUCCESS;
    UINT BufferCount;
    UINT TotalPacketLength=0;
    PNDIS_BUFFER pBuffer;
    PTXQ_NODE    pTQNode;
    PSDIO_TX_PKT_T  pPktBufPt;

    ///Precondition of this function: 
    ///     one and only one of either Packet or pDnldPacket exists
    ASSERT((Packet!=NULL)||(pDnldPacket!=NULL));
    ASSERT(((ULONG)Packet&(ULONG)pDnldPacket) == 0);
    
    EnterCriticalSection(&pTxParam->TxCriticalSection);
    ///
    ///Check the integrity and priority of the packet
    ///
    if (Packet != NULL) {
        NdisQueryPacket(Packet, NULL, &BufferCount, &pBuffer, &TotalPacketLength);
        if(!pBuffer || !BufferCount || !TotalPacketLength)
        {
            ret=TXEQ_FAIL; 
            DBGPRINT(DBG_TX|DBG_WARNING,(L"%s: NDIS buffer is not valid, return FAILURE \n", TEXT(__FUNCTION__)));  
            goto EqFinal;
        }
        ac = GetPktAC(pTxParam, Packet);
    } else {
        TotalPacketLength = pDnldPacket->Len;
        ac = 3;                             /* Default AC: BE*/
    }

    /*
        Dequeue an available TxNode & initialize the buffer pointer
    */
    TxGetNode(pTxParam, ac, TotalPacketLength, &pTQNode);
    if (pTQNode == NULL) {
		PTXQ_NODE       pTQNode;
        /**< Can't find an available TxNode */
		pTQNode = PopFreeTxQNode(pTxParam->TxFreePendQ);
		if( pTQNode == NULL ) {
			//ret = FALSE; 
			ret = TXEQ_FAIL;
			goto EqFinal;
		}
		else
		{
			pTQNode->pPacket = Packet;
			InsertTxQNodeAtTail(&pTxParam->TxPendQKeeper[ac],pTQNode);
			ret = TXEQ_EQ_PENDING_QUEUE;
		} 

        DBGPRINT(DBG_TX|DBG_WARNING,(L"%s: No available TxNode, return FAILURE \n", TEXT(__FUNCTION__)));  
        goto EqFinal;
    }
    /*
        Copy the pacet payload to the buffer in the TxNode
    */
    if (Packet != NULL) {
        ///pTQNode->pPacket = Packet;
        pPktBufPt = (PSDIO_TX_PKT_T)pTQNode->pPktBuf;
    	Status = PrepareDownloadPacket(pTxParam, Packet, pPktBufPt);
        ///pTQNode->DnldPacket.Buf.TxDataBuf.Wcb.Priority = GetPktPriority(pTxParam, Packet);
        pPktBufPt->Wcb.Priority = GetPktPriority(pTxParam, Packet);
    } else {
        NdisMoveMemory(pTQNode->pPktBuf, &pDnldPacket->Buf.TxDataBuf, TotalPacketLength);
    }
    ret = TXEQ_SUCCESS;
    
EqFinal:
    LeaveCriticalSection(&pTxParam->TxCriticalSection);
    return ret; 
}

/**
 *
 *  @fn: TxPacketDeQueue()
 *  @breif: Dequeue the packet to TxQueue
 *  @param: ipTxParam   Pointer to Tx_Param
 *  @param: ppTxQKeeper TxQueue Keeper
 *  @param: ppTQNode    The dequeued TxNode
 *    
 *  Return Value: NDIS_STATUS_SUCCESS or NDIS_STATUS_FAILURE
 * 
 *  Notes:        
 *
 */
static inline VOID TxPacketDeQueue(PTX_PARAM pTxParam,PPTXQ_KEEPER ppTxQKeeper,PPTXQ_NODE ppTQNode, BOOLEAN IsTest)
{ 
    int pri;

    /*
        TXPRYQ[MAX_AC_QUEUES-1] is the priority.
    */
    EnterCriticalSection(&pTxParam->TxCriticalSection);
    for(pri=MAX_AC_QUEUES-1; pri>=0; pri--) // tt wled
    {
        *ppTxQKeeper = (PTXQ_KEEPER)&pTxParam->TXPRYQ[pri];
        if (IsTest == FALSE) {
            *ppTQNode = PopFirstTxQNode(*ppTxQKeeper);
        } else {
            *ppTQNode = (*ppTxQKeeper)->head;
        }
        if(*ppTQNode)
        {
            if (IsTest == FALSE) {
                pTxParam->TxPacketCount-=(*ppTQNode)->PktCnt;
            }
            break;
        }
    }
    if ((*ppTQNode == NULL)&&(IsTest==FALSE)) {
        RETAILMSG(1, (L"%s: tnode=NULL, %d, %d \r\n", TEXT(__FUNCTION__), pTxParam->TxPacketCount, IsTest));
    }
    LeaveCriticalSection(&pTxParam->TxCriticalSection);
    return;
} 

/**
 *
 *  @fn: TxPacketEnQueueAtHead()
 *  @breif: Enqueue the packet to TxQueue at head, handled if failed to send a packet
 *  @param: ipTxParam   Pointer to Tx_Param
 *  @param: ppTxQKeeper TxQueue Keeper
 *  @param: ppTQNode    The dequeued TxNode
 *
 * 
 *  Notes:        
 *
 */
static inline VOID TxPacketEnQueueAtHead(PTX_PARAM pTxParam,PTXQ_KEEPER pTxQKeeper, PTXQ_NODE pTQNode)
{
    EnterCriticalSection(&pTxParam->TxCriticalSection);
    InsertTxQNodeFromHead(pTxQKeeper,pTQNode);
    pTxParam->TxPacketCount+=pTQNode->PktCnt;
    LeaveCriticalSection(&pTxParam->TxCriticalSection);
    return;
}

/**
 *
 *  @fn: ReturnTxNode()
 *  @breif: Return the TxNode back to Tx-Queue
 *  @param: ipTxParam   Pointer to Tx_Param
 *  @param: ppTQNode    The dequeued TxNode
 *
 * 
 *  Notes:        
 *
 */
static inline VOID ReturnTxNode(PTX_PARAM pTxParam, PTXQ_NODE pTQNode)
{
    EnterCriticalSection(&pTxParam->TxCriticalSection);
    PushFreeTxQNode(pTxParam->TxFreeQ,pTQNode);
	EnQueuePending(pTxParam); // DS_driver ++ 
    LeaveCriticalSection(&pTxParam->TxCriticalSection);
    return;
}

/**
 *
 *  @fn: TxPacketDeQueueByLowPriority()
 *  @breif: Dequeue the low priority packet from queue
 *  @param: ipTxParam   Pointer to Tx_Param
 *  @param: ppTxQKeeper TxQueue Keeper
 *  @param: ppTQNode    The dequeued TxNode
 *
 * 
 *  Notes:        
 *
 */
//static ULONG TxPacketDeQueueByLowPriority(PTX_PARAM pTxParam,PPTXQ_KEEPER ppTxQKeeper,PPTXQ_NODE ppTQNode, ULONG ac)
static ULONG TxPacketDeQueueByLowPriority(PTX_PARAM pTxParam, PPTXQ_NODE ppTQNode, ULONG ac)
{           
    ULONG pri;

    for(pri=0; pri<ac; pri++) // tt wled
    {
       //*ppTxQKeeper = (PTXQ_KEEPER)&pTxParam->TXPRYQ[pri];
       //*ppTQNode = PopFirstTxQNode(*ppTxQKeeper);
       *ppTQNode = PopFirstTxQNode(&pTxParam->TXPRYQ[pri]);
       if(*ppTQNode)
       {

          break;
       }
    }
 
    return pri;   
} 

/**
 *
 *  @fn: PrepareDownloadPacket()
 *  @breif: Prepare the DownloadPacket from packet send by upper layer
 *  @param: ipTxParam   Pointer to Tx_Param
 *  @param: Packet      The packet to be sent
 *  @param: pDnldPacket The packet to be sent, changed the format to the one firmware recongnized
 *    
 *  Return Value: None
 * 
 *  Notes:               
 *
 */
///static NDIS_STATUS PrepareDownloadPacket(PTX_PARAM pTxParam,PNDIS_PACKET Packet, PSDIO_TX_PKT pDnldPacket)
static NDIS_STATUS PrepareDownloadPacket(PTX_PARAM pTxParam,PNDIS_PACKET Packet, PSDIO_TX_PKT_T pDnldPacket)
{
    NDIS_STATUS  Status = NDIS_STATUS_SUCCESS;
    UINT BufferCount;
    UINT TotalPacketLength=0;
    UINT TotalBufferLength=0;
    UINT BytesCopied=0;
    UINT Length,AccumLength;
    PNDIS_BUFFER pBuffer, pNextBuffer;
    ///PVOID pVirtualAddr;
	PUCHAR pVirtualAddr;
    PUCHAR pHeader = NULL;
    ULONG coBufferLength = 0;
    ULONG i;
    PWCB pWcb;
    ///UCHAR *pCurPtr = pDnldPacket->Buf.CmdBuf;
    UCHAR *pCurPtr = (UCHAR *)pDnldPacket;

    Status = NDIS_STATUS_SUCCESS;
    NdisQueryPacket(Packet,
                    NULL,
                    &BufferCount,
                    &pBuffer,
                    &TotalPacketLength);

    if(!pBuffer || !BufferCount || !TotalPacketLength)
    {
        Status=NDIS_STATUS_FAILURE; 
        DBGPRINT(DBG_TX|DBG_WARNING,(L"TX - NDIS buffer is not valid, return FAILURE \n"));  
        return Status;
    }
    NdisQueryBuffer(pBuffer, &pVirtualAddr, &Length);
    pHeader = (PUCHAR)pVirtualAddr;

/*#ifndef MRVL_CHECK_TXPACKET
#ifdef WPA
    // check if in key absent state, if so, block all packet other than
    // 802.1x

    if ( Adapter->EncryptionStatus == Ndis802_11Encryption2KeyAbsent )  
    {
        // no key yet, only allow 802.1x packets
        if ( TotalPacketLength < 14 ) 
        {
            // malformed packet, blocked!
            DBGPRINT(DBG_TX|DBG_WARNING,(L"Got packet with size less than 14 bytes, reject!\n"));
            return NDIS_STATUS_FAILURE;
        }

        if ( (pHeader[12] != 0x88) || (pHeader[13] != 0x8E) )
        {
            DBGPRINT(DBG_TX|DBG_WARNING,(L"Still no key and packet type(0x%x 0x%x)is not 802.1x , drop!\n",
                                  pHeader[12],
                                  pHeader[13]));
            return NDIS_STATUS_FAILURE;
        }
    }

  
#endif // #ifdef WPA
#endif //ifdef MRVL_CHECK_TXPACKET
*/
    DBGPRINT(DBG_TX|DBG_HELP,(L"SendSinglePacket: buffers %d, packet len %d\n",BufferCount, TotalPacketLength));
    ///pWcb = (PWCB)&(pDnldPacket->Buf.TxDataBuf.Wcb);
    pWcb = &pDnldPacket->Wcb;
//pWcb->CheckEnd = pWcb->CheckBgn = 0x55aa55aa;
    pWcb->Status =    MRVDRV_WCB_STATUS_USED;
    pWcb->PktLen = (USHORT)TotalPacketLength;
    pWcb->TxControl |= pTxParam->TX_Control_Value; 
    pWcb->PktPtr = sizeof(WCB);
 
  
    //      First buffer contains the MAC header
    //      Call NdisMoveMemory() to copy DEST MAC adress to WCB
    NdisMoveMemory((PVOID)&(pWcb->DestMACAdrHi),
                    pVirtualAddr, 
					2);
	NdisMoveMemory((PVOID)&(pWcb->DestMACAdrLow),
                    pVirtualAddr+2, 
                    MRVDRV_ETH_ADDR_LEN-2);

    {
        ULONG currenttime, packettime;
	
        currenttime = GetTickCount(); 
        packettime = *((ULONG *)(&Packet->MacReserved[0]));
        // PKtdelay in driver layer , unit 2ms
        pWcb->Reserved[0]=(UCHAR)((currenttime - packettime) / 2);
        DBGPRINT(DBG_CCX_V4,(L"PktDelay%02x ",pWcb->Reserved[0]));    
    }

    // Start the packet.
    TotalBufferLength = TotalPacketLength;
    TotalBufferLength += sizeof(WCB);
  
    // TotalBufferLength contains the size of the packet
    ///pDnldPacket->Len = ADD_SDIO_PKT_HDR_LENGTH(TotalBufferLength); 
    ///*PktLen = ADD_SDIO_PKT_HDR_LENGTH(TotalBufferLength); 

    DBGPRINT(DBG_TX,(L"TX %d bytes: packet size %d\n",(ULONG)TotalBufferLength, (ULONG)TotalPacketLength));
    pCurPtr += sizeof(WCB);
    AccumLength = sizeof(WCB);
    //      Query each buffer for the packet and move data to SQ
    for(i=0; i<BufferCount; i++)
    {
        NdisQueryBuffer(pBuffer, &pVirtualAddr, &Length);
        AccumLength += Length;
        if( AccumLength > MRVDRV_ETH_TX_PACKET_BUFFER_SIZE )        // PJG: accum length already counts the header... need to compare to entire buffer size
        {
            break;
        }
       
        if( pVirtualAddr )
        {
            NdisMoveMemory(pCurPtr, (PUCHAR)pVirtualAddr, (USHORT)Length);
            pCurPtr += Length;
        }
     
        NdisGetNextBuffer(pBuffer, &pNextBuffer);
        if( !pNextBuffer )
        {
            break;
        }
        pBuffer = pNextBuffer;
    }//for(i=0; i<BufferCount; i++)

    DBGPRINT(DBG_TX|DBG_HELP,(L"[Marvell]TX SendSinglePacket!\n"));  
    ///pDnldPacket->Type = IF_DATA_PKT;

    return NDIS_STATUS_SUCCESS;
}

/**
 *
 *  @fn: CCX_SendSinglePacket()
 *  @breif: Send one CCX Packet
 *  @param: ipTxParam   Pointer to Tx_Param
 *  @param: Packet      The packet to be sent
 *    
 *  Return Value: None
 * 
 *  Notes:               
 *
 */
NDIS_STATUS CCX_SendSinglePacket(IN PVOID	ipTxParam, IN PNDIS_PACKET Packet)
{
    PTX_PARAM pTxParam = (PTX_PARAM)ipTxParam;
    NDIS_STATUS     status;
    SDIO_TX_PKT     downloadPkt;
    UCHAR           *pCurPtr = (UCHAR *)downloadPkt.Buf.CmdBuf;
    UINT            TotalPacketLength=0;
    UINT            TotalBufferLength=0;
    UINT            Length;
    UINT            BufferCount;
    PNDIS_BUFFER    pBuffer;
    //PVOID           pVirtualAddr;
	PUCHAR          pVirtualAddr;
    WCB             LocalWCB;


    NdisQueryPacket(Packet, NULL, &BufferCount, &pBuffer, &TotalPacketLength);
    if(!pBuffer || !BufferCount || !TotalPacketLength) {
        DBGPRINT(DBG_CCX|DBG_HELP,(L"TX - NDIS buffer is not valid, return FAILURE \n"));
        status=NDIS_STATUS_FAILURE; 
        goto end;
    } else {
        DBGPRINT(DBG_CCX|DBG_HELP,(L"BufferCount: %d\n", BufferCount));
    }
    NdisQueryBuffer(pBuffer, &pVirtualAddr, &Length);

    ///
    ///Fill the WCB
    ///
    NdisZeroMemory(&LocalWCB, sizeof(WCB));
    LocalWCB.Status = MRVDRV_WCB_STATUS_USED;
    LocalWCB.PktLen = (USHORT)Length;
    LocalWCB.TxControl  = 0;
    LocalWCB.PktPtr = sizeof(WCB);
    ///downloadPkt.Buf.TxDataBuf.Wcb.PowerMgmt = MRVDRV_WCB_POWER_MGMT_LAST_PACKET;
    SetLastPkt(TxPKTAggreCnt, &downloadPkt);
    NdisMoveMemory((PVOID)LocalWCB.DestMACAdrHi,
                        pVirtualAddr, 
						2);
	NdisMoveMemory((PVOID)LocalWCB.DestMACAdrLow,
                        pVirtualAddr+2, 
                        MRVDRV_ETH_ADDR_LEN-2);
    NdisMoveMemory(pCurPtr, (PUCHAR)&LocalWCB, sizeof(WCB));
    pCurPtr += sizeof(WCB);
    
    ///
    /// Fill in the packet data content
    ///
    NdisMoveMemory(pCurPtr, (PUCHAR)pVirtualAddr, (USHORT)Length);
    pCurPtr += Length;
    TotalBufferLength = Length;
    TotalBufferLength += sizeof(WCB);
    downloadPkt.Len = ADD_SDIO_PKT_HDR_LENGTH(TotalBufferLength);
    downloadPkt.Type = IF_DATA_PKT;                 

    DBGPRINT(DBG_CCX|DBG_HELP,(L"CCX_SendSinglePacket, calling If_DownloadPkt\n"));
    status = If_DownloadPkt(pTxParam->pAdapter, &downloadPkt);
    if (!SD_API_SUCCESS(status))
    {                
        DBGPRINT(DBG_CCX|DBG_HELP,(L"[Marvell]SendSinglePacket:TX download failed! "));  
        status = NDIS_STATUS_FAILURE;
        goto end;
    } else {
        DBGPRINT(DBG_CCX|DBG_HELP,(L"CCX_SendSinglePacket, If_DownloadPkt success\n"));
    }
end:
    return status;
}


VOID CheckPktFormat(PTXQ_NODE    pTQNode)
{
    PSDIO_TX_PKT    pPktStruct = &pTQNode->DnldPacket;
    PUCHAR          pBufPt = (PUCHAR)(&pPktStruct->Buf.TxDataBuf);
    PSDIO_TX_PKT_T   pPktPt = (PSDIO_TX_PKT_T)pBufPt;
    USHORT          i;
    USHORT          AccuLen=0;
    USHORT		    TailLen, PadLen, PktLen, NextWcbVal;
    UCHAR           AggrNo;

//    if (pTQNode->PktCnt > 1) {
//        RETAILMSG(1, (L"AggrPkt: %d\r\n", pTQNode->PktCnt));
//    }
    if (pPktStruct->Len == 0) {
        RETAILMSG(1, (L"%s: Invalid Lenth: %d\r\n", TEXT(__FUNCTION__), pPktStruct->Len));
    }
    if (pPktStruct->Type != DNLDT_TX) {
        RETAILMSG(1, (L"%s: Invalid type: %d\r\n", TEXT(__FUNCTION__), pPktStruct->Type));
    }
    for (i=0 ; i<pTQNode->PktCnt ; i++) {
        PktLen = pPktPt->Wcb.PktLen;
        AccuLen += PktLen;
        AggrNo = (UCHAR)((pPktPt->Wcb.TxControl & 0x00030000)>>16);
//        if (pTQNode->PktCnt > 1) {
//            RETAILMSG(1, (L"Pkt[%d]: %xh\r\n", AggrNo, pPktPt->Wcb.NextWCBPtr));
//        }
        if (AggrNo != i) {
            RETAILMSG(1, (L"%s: Invalid AggrNo:%d, expect %d, TxCtrl: %xh\r\n", TEXT(__FUNCTION__),
                            AggrNo, i, pPktPt->Wcb.TxControl));
        }
        if ((pPktPt->Wcb.NextWCBPtr%4) != 0) {
            RETAILMSG(1, (L"%s: NextWCBPtr:%d, not align with 4\r\n", TEXT(__FUNCTION__),
                            pPktPt->Wcb.NextWCBPtr));
        }
        if (i == pTQNode->PktCnt-1) {
            if (pPktPt->Wcb.NextWCBPtr != 0) {
                RETAILMSG(1, (L"%s: Invalid NextWCBPtr: %d, expect 0\r\n", TEXT(__FUNCTION__),
                            pPktPt->Wcb.NextWCBPtr));
            }
        } else {
            TailLen = ((PktLen+sizeof(WCB)) + FM_TX_PKT_GAP_LEN)%4;
            PadLen = (TailLen == 0)?0:(4-TailLen);
            NextWcbVal = (PktLen+sizeof(WCB))+FM_TX_PKT_GAP_LEN+PadLen;
            if (pPktPt->Wcb.NextWCBPtr != NextWcbVal) {
                RETAILMSG(1, (L"%s: Invalid NextWCBPtr:%d, Expect: %d, PktLen: %d\r\n", TEXT(__FUNCTION__),
                                pPktPt->Wcb.NextWCBPtr,
                                NextWcbVal,
                                PktLen));
            }
            pBufPt += pPktPt->Wcb.NextWCBPtr;
            pPktPt = (PSDIO_TX_PKT_T)pBufPt;
        }
        
    }
}
/**
 *
 *  @fn: SendOneQueuedPacket()
 *  @breif: Send one queued Packet
 *  @param: ipTxParam   Pointer to Tx_Param
 *    
 *  Return Value: None
 * 
 *  Notes:               
 *
 */
void SendOneQueuedPacket(PVOID	ipTxParam)
{
    PTX_PARAM pTxParam = (PTX_PARAM)ipTxParam;
    ///PNDIS_PACKET pPacket;
    NDIS_STATUS Status;
    USHORT usTxFrmSeqNum = 0, usTxFrmStatus = 0, usTCNSeqNum = 0;
    USHORT usTxCurrentRate = 0;
    PTXQ_KEEPER  pTxQKeeper;
    PTXQ_NODE    pTQNode;
    int DnLdTicket;

    {
        wlan_11h_state_t* pState11h = &pTxParam->pAdapter->state11h;
        if (pState11h->txDisabled == TRUE)
        {
            DBGPRINT(DBG_11H|DBG_TX, (L"11h: Tx Packet not allowed.\r\n"));
            ///return NDIS_STATUS_RESOURCES;
			return;
        }
    }

    if (pTxParam->TxPacketCount == 0) {
        ///Nothing to send. Leave now
        goto FuncFinal;
    }

    DnLdTicket = RequestDnldTicket(pTxParam->pAdapter->pDnLdParam, DNLDT_TX, SetTxSoftIntr, pTxParam->pAdapter);
    ///DnLdTicket = RequestDnldTicket(pTxParam->pAdapter->pDnLdParam, DNLDT_TX, ContinueTxProcess, pTxParam);
    if (DnLdTicket == 0) {
        ///Download Path is occurped. Wait until the next run
        SetMsg(&pTxParam->TxDbgMsg, L"GetTxTicket_fail");
//RETAILMSG(1, (L"%s, DnldTicket=0\r\n", TEXT(__FUNCTION__)));
        goto FuncFinal;
    }
//RETAILMSG(1, (L"%s, DnldTicket=%d\r\n", TEXT(__FUNCTION__), DnLdTicket));
    pTxParam->DnLdTicket = DnLdTicket;

    TxPacketDeQueue(pTxParam, &pTxQKeeper, &pTQNode, FALSE);
    if (pTQNode == NULL) {
        RETAILMSG(1, (L"Warning: %s, Can not get TxQNode\r\n", TEXT(__FUNCTION__)));
        ReleaseDnldTicket(pTxParam->pAdapter->pDnLdParam, pTxParam->DnLdTicket);
        goto FuncFinal;
    }
    //RETAILMSG(1, (L"%s: Sending_pkt %d, %d\r\n", TEXT(__FUNCTION__), 
    //                                        pTQNode->PktCnt,
    //                                        pTQNode->DnldPacket.Len));
    ///This function is for debug checking only
    CheckPktFormat(pTQNode);
    {
    USHORT      statid;
    pTxParam->TxPktStats[TxPKTAggreCnt] ++;
    pTxParam->TxPktStats[pTQNode->PktCnt-1] ++;
    if (pTxParam->TxPktStats[TxPKTAggreCnt] >= 100000) {
        RETAILMSG(1, (L"TxAggr_Statistics: ("));
        for (statid=0 ; statid<(TxPKTAggreCnt+1) ; statid++) {
            RETAILMSG(1, (L" %d, ", pTxParam->TxPktStats[statid]));
            pTxParam->TxPktStats[statid] = 0;
        }
        RETAILMSG(1, (L")\r\n"));
    }
    }
    Status = SendSinglePacket(pTxParam, (PNDIS_PACKET)0xabcd,&(pTQNode->DnldPacket));
    if(Status == NDIS_STATUS_SUCCESS)
    {
        pTxParam->TxDnldPktCnt +=pTQNode->PktCnt;
        ReturnTxNode(pTxParam, pTQNode);
    }
    else
    {
        //Error handling , push back this node 
        DBGPRINT(DBG_ERROR,(L"[TxDone] Send packet fail\r\n"));
        TxPacketEnQueueAtHead(pTxParam, pTxQKeeper, pTQNode);
        ///If failed to download the packet, release the Dnld ticket Tx hold
        ReleaseDnldTicket(pTxParam->pAdapter->pDnLdParam, pTxParam->DnLdTicket);
    }

FuncFinal:
	return;
}

// DS_driver ++
static void EnQueuePending(PTX_PARAM pTxParam)
{
    PTXQ_NODE    pTQNode=NULL;
    PTXQ_NODE    pTPendQNode=NULL;
    PNDIS_PACKET Packet=NULL;
    BOOLEAN ret;
    int i;

    if(!pTxParam->TxFreeQ)
        return;
                                 
    for (i=0; i<=3 && pTPendQNode==NULL; i++ )
    { 
      pTPendQNode = PopFirstTxQNode(&pTxParam->TxPendQKeeper[i]);
    }
                     
    if( pTPendQNode == NULL )
         return;
    else
    {
        Packet = pTPendQNode->pPacket;
        pTPendQNode->pPacket = NULL;
        PushFreeTxQNode(pTxParam->TxFreePendQ,pTPendQNode); 
    }

    EnterCriticalSection(&pTxParam->TxCriticalSection);
    ret = TxPacketEnQueue(pTxParam, Packet, NULL);
    
    if( ret != TXEQ_SUCCESS )
    {
		RETAILMSG(1,(L"Abnormal Condiation.......\n\r"));
    }
    else
    {
		RETAILMSG(1,(L"Complete a packet:%x\n\r",(ULONG)Packet));                 
		pTxParam->TxPacketCount++;
		NdisMSendComplete(pTxParam->MrvDrvAdapterHdl, Packet, NDIS_STATUS_SUCCESS);  
    }
    LeaveCriticalSection(&pTxParam->TxCriticalSection);
}
// DS_driver --


#define PKTPTR_INITVAL      0xffff
/**
 *
 *  @fn: InitTxParam()
 *  @breif: Initialize TxParam
 *  @param: ipTxParam   Pointer to Tx_Param
 *  @param: pTxInit     Necessary parameters to initialize Tx_Param
 *    
 *  Return Value: None
 * 
 *  Notes:               
 *
 */
NDIS_STATUS InitTxParam(PPVOID	ipTxParam, PTXINIT pTxInit)
{
    NDIS_STATUS result = NDIS_STATUS_SUCCESS;
    PTX_PARAM	pTxParam;

    pTxParam = *ipTxParam = (PVOID)&TxParam;
    pTxParam->pAdapter = pTxInit->pAdapter;
    pTxParam->MrvDrvAdapterHdl = pTxInit->MrvDrvAdapterHdl;
    /// => Set this limitation in case the registry value is too big
    pTxParam->MaxTxAggrCnt = (pTxInit->MaxTxAggrCnt<=TxPKTAggreCnt)?(pTxInit->MaxTxAggrCnt):(TxPKTAggreCnt);
    ///InitTxNode(&pTxParam->TxNode, &pTxParam->Wcb);
    pTxParam->Wcb.PktPtr = PKTPTR_INITVAL;
    InitializeTxNodeQ(pTxParam);
    InitializeCriticalSection(&pTxParam->TxCriticalSection);
    // Tx related varables
    pTxParam->SentPacket = NULL;
    pTxParam->TxDnldPktCnt = 0;
    RETAILMSG(1, (L"MaxTxAggrCnt: %d\r\n", pTxParam->MaxTxAggrCnt));
    InitDbgMsg(&pTxParam->TxDbgMsg);
    SetMsg(&pTxParam->TxDbgMsg, L"Init_TxModule");

    return result;
}

/**
 *
 *  @fn: DeinitTxParam()
 *  @breif: Deinitialize TxParam
 *  @param: ipTxParam   Pointer to Tx_Param
 *    
 *  Return Value: None
 * 
 *  Notes:               
 *
 */
NDIS_STATUS	DeinitTxParam(PVOID ipTxParam)
{
    PTX_PARAM pTxParam = (PTX_PARAM)ipTxParam;
    NDIS_STATUS result = NDIS_STATUS_SUCCESS;
    PEXECCMDNODE	pNodePt;

    DeleteCriticalSection(&pTxParam->TxCriticalSection);
    pNodePt = pTxParam->pFuncBeforeTx;
    while (pNodePt != NULL) {
        MRVDRV_FREE_MEM((PVOID)pNodePt, sizeof(EXECCMDNODE));
        pNodePt = pNodePt->pNext;  
    }
    pTxParam->pFuncBeforeTx = NULL;

    pNodePt = pTxParam->pFuncAfterTx;
    while (pNodePt != NULL) {
        MRVDRV_FREE_MEM((PVOID)pNodePt, sizeof(EXECCMDNODE));
        pNodePt = pNodePt->pNext; 
    }
    pTxParam->pFuncAfterTx = NULL;

    SetMsg(&pTxParam->TxDbgMsg, L"DeInit_TxModule");
    return result;
}

/**
 *
 *  @fn: GetTxCtrlValue()
 *  @breif: Return the saved TX_Control_Value
 *  @param: ipTxParam   Pointer to Tx_Param
 *    
 *  Return Value: None
 * 
 *  Notes:               
 *
 */
ULONG	GetTxCtrlValue(PVOID	ipTxParam)
{
    PTX_PARAM pTxParam = (PTX_PARAM)ipTxParam;
    return pTxParam->TX_Control_Value;
}

/**
 *
 *  @fn: TxCheckForHang()
 *  @breif: Check periodically to monitor the tx process
 *  @param: ipTxParam   Pointer to Tx_Param
 *    
 *  Return Value: BOOLEAN, If tx-timeout or not
 * 
 *  Notes:               
 *
 */
BOOLEAN TxCheckForHang(PVOID	ipTxParam)
{
    BOOLEAN		IsTimeout = FALSE;
    PTX_PARAM pTxParam = (PTX_PARAM)ipTxParam;
    static int LastTxDnldPktCnt = 0;
    static int LastTxSentPktId=0;
    //static short PeriodCheck=5;

    if ((pTxParam->TxPacketSend == LastTxSentPktId)&&
        (pTxParam->TxPacketCount == 0)) {
        ///No Tx-Packet is pending & no further Tx-packet is request to be sent
        goto FuncFinal;
    }
    LastTxSentPktId = pTxParam->TxPacketSend;

    if ( pTxParam->SentPacket )
    {
        pTxParam->dwTxTimeoutCount ++;
        if ( pTxParam->dwTxTimeoutCount > MRVDRV_TX_TIMEOUT_COUNT_THRESHOLD )
        {
            DBGPRINT( DBG_ERROR, (L"Tx timeout!\r\n"));
            IsTimeout = TRUE;
            RETAILMSG(1, (L"Tx timeout \r\n"));
            /*{
            int i;
            for (i=0 ; i<MAXMSGSIZE ; i++) {
                RETAILMSG(1, (L"%d: %s\r\n", i, msgbuf[(msgid+i+1)%MAXMSGSIZE]));
            }
            }
            */
            ShowDnldStatus(pTxParam->pAdapter->pDnLdParam);
            DumpMsg(&pTxParam->TxDbgMsg);
        }
    }

	///To check the Tx-flow => For debug purpose only
	if ((pTxParam->TxPacketCount > 0) && 
		(pTxParam->TxDnldPktCnt == LastTxDnldPktCnt)) {
		///If the TxDnldPktCnt does not change between 2 CheckForHang => No new packets were downloaded during this period
		/// & if TxPacketCount > 0 => Tx flow has been suspended
		//	=> Show the debug message
		RETAILMSG(1, (L"==> Warning, Tx may be suspended. PendingPktCnt: %d\r\n", pTxParam->TxPacketCount));
		ShowDnldStatus(pTxParam->pAdapter->pDnLdParam);
        DumpMsg(&pTxParam->TxDbgMsg);
        RETAILMSG(1, (L"TxDnldTicket: %d\r\n", pTxParam->DnLdTicket));
	}
	LastTxDnldPktCnt = pTxParam->TxDnldPktCnt;
	//PeriodCheck --;
FuncFinal:
	return IsTimeout;
}

/**
 *
 *  @fn: IsTxPktSending()
 *  @breif: Check if is there a packet which is being sent
 *  @param: ipTxParam   Pointer to Tx_Param
 *    
 *  Return Value: BOOLEAN, If a packet is being sent
 * 
 *  Notes:               
 *
 */
/*
BOOLEAN	IsTxPktSending(PVOID	ipTxParam)
{
	PTX_PARAM pTxParam = (PTX_PARAM)ipTxParam;
	return (pTxParam->SentPacket)?(TRUE):(FALSE);
}*/


/**
 *
 *  @fn: EmptyTxQueue()
 *  @breif: Clean up the Tx-Queue
 *  @param: ipTxParam   Pointer to Tx_Param
 *    
 *  Return Value: None
 * 
 *  Notes:               
 *
 */
void EmptyTxQueue(PVOID	ipTxParam)
{
    PTX_PARAM pTxParam = (PTX_PARAM)ipTxParam;
    PTXQ_KEEPER  pTxQKeeper;
    PTXQ_NODE    pTQNode;
    ///PNDIS_PACKET	pPacket;
    NDIS_STATUS     Status = NDIS_STATUS_FAILURE;
    
    while(1)
    { 
        TxPacketDeQueue(pTxParam, &pTxQKeeper, &pTQNode, FALSE); 
        if( pTQNode == NULL )
        {
            break;
        }

        ///It should not be necessary to set complete because for NDIS's point of view
        //      this packet has been sent
        //
        //pPacket = pTQNode -> pPacket;   
        //NDIS_SET_PACKET_STATUS(pPacket, Status);
        //NdisMSendComplete(
        //                        pTxParam->MrvDrvAdapterHdl,
        //                        pPacket,
        //                        Status);    
		ReturnTxNode(pTxParam, pTQNode);
    }
    
	return;
}

/**
 *
 *  @fn: EmptyTxPendingPkt()
 *  @breif: Remove the current sending packet
 *  @param: ipTxParam   Pointer to Tx_Param
 *    
 *  Return Value: None
 * 
 *  Notes:               
 *
 */
void	EmptyTxPendingPkt(PVOID	ipTxParam)
{
    PTX_PARAM pTxParam = (PTX_PARAM)ipTxParam;
    pTxParam->SentPacket = NULL;
    return;
}

/**
 *
 *  @fn: IsTxQueueEmpty()
 *  @breif: Check if the Tx-Queue is empty or not
 *  @param: ipTxParam   Pointer to Tx_Param
 *    
 *  Return Value: BOOLEAN, Is the queue empty or not
 * 
 *  Notes:               
 *
 */
/*
BOOLEAN	IsTxQueueEmpty(PVOID	ipTxParam)
{
    int i;
    PTX_PARAM pTxParam = (PTX_PARAM)ipTxParam;
    BOOLEAN result = TRUE;

    for (i = AC_PRIO_BE; i < MAX_AC_QUEUES; i++)
    {
        if (!IsTxQEmpty(&pTxParam->TXPRYQ[i])) {
            result = FALSE;
            break;
        }
    }
    return result;
}*/

/**
 *
 *  @fn: RegisterFilterFuncForTx()
 *  @breif: Register some Tx filter functions which will be called before sending the packet
 *  @param: ipTxParam   Pointer to Tx_Param
 *  @param: func        The registered function
 *  @param: pFuncArg    The argument of the function
 *    
 *  Return Value: NDIS_STATUS
 * 
 *  Notes:               
 *
 */
NDIS_STATUS RegisterFilterFuncForTx(IN PVOID	ipTxParam, TXFILTER_TYPE TFType, PKTFILTERFUNC func, PVOID pFuncArg)
{
    PTX_PARAM pTxParam = (PTX_PARAM)ipTxParam;
    NDIS_STATUS 	Status;
    PEXECCMDNODE	pChkFuncNode;
	PEXECCMDNODE	*ppNodePt;

    switch (TFType) {
        case TF_BEFORE:
            ppNodePt = &pTxParam->pFuncBeforeTx;
            break;
        case TF_AFTER:
            ppNodePt = &pTxParam->pFuncAfterTx;
            break;
        default:
            Status = NDIS_STATUS_FAILURE;
            goto FuncFinal;
    } 
    
	
    Status = MRVDRV_ALLOC_MEM(&pChkFuncNode, sizeof(EXECCMDNODE));
    if (Status != NDIS_STATUS_SUCCESS) 
    {
        goto FuncFinal;
    }

    if (*ppNodePt == NULL) 
    {
        ///The list is empty
    	*ppNodePt = pChkFuncNode;
    } 
    else 
    {
        ///The list is not empty => Chain it to the end of the list
        while ((*ppNodePt)->pNext != NULL) 
        {
            ppNodePt = &((*ppNodePt)->pNext);
        }
        (*ppNodePt)->pNext = pChkFuncNode;
    }

    pChkFuncNode->pNext = NULL;
    pChkFuncNode->func.ExecFunc = func;
    pChkFuncNode->func.FuncArg = pFuncArg;
	
FuncFinal:
	return Status;
}

/**
 *
 *  @fn: ReadyForNextTxPacket()
 *  @breif: Check if the tx process is ready for sending the next packet
 *  @param: ipTxParam   Pointer to Tx_Param
 *    
 *  Return Value: BOOLEAN: is Tx-process is ready or not
 * 
 *  Notes:               
 *
 */
/*
BOOLEAN	ReadyForNextTxPacket(IN PVOID	ipTxParam)
{
    PTX_PARAM pTxParam = (PTX_PARAM)ipTxParam;
    BOOLEAN	result;
	
    EnterCriticalSection(&pTxParam->TxCriticalSection);
    if ((pTxParam->TxPacketCount > 0) && 
        (IsTxPktSending(ipTxParam) == FALSE) 
#ifdef SENDPKTS
        && (pTxParam->NeedToWait == FALSE)
#endif ///SENDPKTS
        ) {
        result = TRUE;
	} else {
        result = FALSE;
    }
    ///RETAILMSG(1, (L"%s: TxPacketCount=%d, %d\r\n", TEXT(__FUNCTION__), pTxParam->TxPacketCount, result));
    LeaveCriticalSection(&pTxParam->TxCriticalSection);
    return result;
}
*/
/**
 *
 *  @fn: ContinueTxProcess()
 *  @breif: Continue the next Tx-Process
 *  @param: ipTxParam   Pointer to Tx_Param
 *    
 *  Return Value: None
 * 
 *  Notes:               
 *
 */
/*
void ContinueTxProcess(IN PVOID ipTxParam)
{
    PTX_PARAM pTxParam = (PTX_PARAM)ipTxParam;

    if (ReadyForNextTxPacket(pTxParam) == TRUE)
    {
        SetTxSoftIntr(pTxParam->pAdapter);
        //SetMsg(&pTxParam->TxDbgMsg, L"Continue_Tx");
    }
    return;
}
*/
/*
int GetTxPendingPktCnt(IN PVOID ipTxParam)
{
    PTX_PARAM pTxParam = (PTX_PARAM)ipTxParam;
    return pTxParam->TxPacketCount;
}
*/
/*
UINT GetTxPktLen(IN PVOID ipTxParam)
{
    PTX_PARAM pTxParam = (PTX_PARAM)ipTxParam;
    PTXQ_KEEPER  pTxQKeeper;
    PTXQ_NODE    pTQNode;
    UINT            PktLen;
    
    TxPacketDeQueue(pTxParam, &pTxQKeeper, &pTQNode, TRUE);
    if (pTQNode != NULL) {
        PktLen = pTQNode->DnldPacket.Len;
    } else {
        PktLen = 0;
    }
    return PktLen;
}
*/
VOID SetTxAggrCnt(IN PVOID ipTxParam, USHORT AggrCnt)
{
    PTX_PARAM pTxParam = (PTX_PARAM)ipTxParam;
    ///Checking for low_boundary
    if (AggrCnt == 0) {
        AggrCnt = 1;
    }
    ///Checking for up_boundary
    if (AggrCnt > TxPKTAggreCnt) {
        AggrCnt = TxPKTAggreCnt;
    }
    pTxParam->MaxTxAggrCnt = AggrCnt;
    return;
}

VOID GetTxStatus(IN PVOID ipTxParam, OUT PTR_TX_STATE pTxState)
{
    PTX_PARAM pTxParam = (PTX_PARAM)ipTxParam;
    pTxState->TxPendCnt = pTxParam->TxPacketCount;
    pTxState->state = (pTxParam->SentPacket==NULL)?(TX_IDLE):(TX_SENDING);
    return;
}


